Each track starts with byte that is used to figure out the start of the message.
Each track ends with byte that is used to figure out the end of the message.
// Visual feedback when the card is being read...
#defineREAD_LED12#defineERROR_LED11#defineMAGSTRIPE_RDT2/* (RDT) data pin (blue) */#defineMAGSTRIPE_RCL3/* (RCL) clock pin (green) */#defineMAGSTRIPE_CLS4/* (CLS) card present pin (yellow) */#defineTRACK_NUM2/*
* Track 3 is the one that can contain the most characters (107).
* We add one more to accommodate the final '\0', as the data is a C string...
*/staticconst byte DATA_BUFFER_LEN =108;staticchar data[DATA_BUFFER_LEN];// Variables used by the interrupt handlers...
staticvolatilebool next_bit =0;// next bit to read
staticvolatileunsignedchar bits[DATA_BUFFER_LEN];// buffer for bits being read
staticvolatileshort num_bits =0;// number of bits already read
voiddata_callback(){
next_bit =!next_bit;}voidclock_callback(){// Avoid a crash in case there are too many bits (garbage)...
if(num_bits >=(DATA_BUFFER_LEN *8)){return;}//Get Character and Bit Index
int char_index = num_bits /8;int bit_index = num_bits %8;//Add the Bit to the buffer
bits[char_index]|=(next_bit << bit_index);//Increase the bit number
num_bits++;}voidsetup(){//Setup Read and Error LEDs
pinMode(READ_LED, OUTPUT);pinMode(ERROR_LED, OUTPUT);//Set LEDs OFF
digitalWrite(READ_LED, LOW);digitalWrite(ERROR_LED, LOW);//Setup Serial to Computer
Serial.begin(9600);//Setup Input from Mag Stripe
pinMode(MAGSTRIPE_RDT, INPUT);pinMode(MAGSTRIPE_RCL, INPUT);pinMode(MAGSTRIPE_CLS, INPUT);//Setup Interrupts
//Reading is more reliable when using interrupts...
attachInterrupt(digitalPinToInterrupt(MAGSTRIPE_RDT), data_callback, CHANGE);// data pin
attachInterrupt(digitalPinToInterrupt(MAGSTRIPE_RCL), clock_callback, FALLING);// clock pin
}shortfind_start_byte(unsignedcharpattern){unsignedchar bit_accum =0;unsignedchar bit_length =(TRACK_NUM ==1?7:5);unsignedchar new_bit;for(short i =0; i < num_bits; i++){
bit_accum >>=1;
bit_accum |=bitRead(bits[i /8],(i %8))<<(bit_length -1);// ...and add the current bit
// Stop when the start sentinel pattern is found...
if(bit_accum == pattern){return i -(bit_length -1);}}// No start sentinel was found...
return-1;}shortdecode_magdata(char*data,unsignedcharsize){unsignedchar start_byte =(TRACK_NUM ==1?0x45:0x0b);unsignedchar chars =0;unsignedchar bit_length =(TRACK_NUM ==1?7:5);//Find the sentinel that denotes the start of the magstripe data
short start_bit_index =find_start_byte(start_byte);
Serial.print("Start Byte: ");
Serial.println(start_bit_index);if(start_bit_index <0){return-2;}//Convert Bits to data
for(short bit_count = start_bit_index; bit_count < num_bits; bit_count += bit_length){unsignedchar bit_accum =0;//Read the first bit_length bytes
for(short j =0; j < bit_length; j++){short idx = bit_count + j;
bit_accum |=(bitRead(bits[idx /8],(idx %8))<< j);}//Check if too long for buffer
if(chars >= size){return-3;}// Check if reached the end of the data
if(bit_accum ==0){break;}//Do Parity Check
unsignedchar parity =0;for(unsignedchar j =0; j <8; j++){
parity +=(bit_accum >> j)&1;}// The parity must be odd...
if(parity %2==0){return-4;}//Remove the Parity bit from the bit_accum
bit_accum &=~(1<<(bit_length -1));// Convert the character to ASCII...
data[chars]=(TRACK_NUM ==1? bit_accum +0x20: bit_accum +0x30);
Serial.print(data[chars]);
chars++;}return chars;}voidreset_data(){//Reset Variables
num_bits =0;
next_bit =0;//Reset Buffers
for(short i =0; i<DATA_BUFFER_LEN; i++){
data[i]=0;
bits[i]=0;}}voidloop(){//Check if card present pin is Low
if(digitalRead(MAGSTRIPE_CLS)== LOW){
Serial.println("Error: Card Not Available");return;}//Device Is Detected Set Read LED ON
Serial.println("Card Available");digitalWrite(READ_LED, HIGH);//Wait for Card Input
while(digitalRead(MAGSTRIPE_CLS)!= LOW){}//Wait for reset of card reader
while(digitalRead(MAGSTRIPE_CLS)== LOW){}// Show that the card has finished reading...
digitalWrite(READ_LED, LOW);
Serial.print("Bits Read: ");
Serial.println(num_bits);//Decode Data
short ret =decode_magdata(data, DATA_BUFFER_LEN);if(ret <0){//Reverse Bits and try again
//Happens when swipped the opposite way
for(short i =0; i < num_bits /2; i++){bool tmp =bitRead(bits[i /8],(i %8));bool tmp2 =bitRead(bits[(num_bits - i)/8],((num_bits - i)%8));//Swap bits
bitWrite(bits[i /8],(i %8), tmp2);bitWrite(bits[(num_bits - i)/8],((num_bits - i)%8), tmp);}//Decode Data
ret =decode_magdata(data, DATA_BUFFER_LEN);}// Send the data to the computer...
Serial.println();
Serial.print("Return Value: ");
Serial.println(ret);// If there was an error reading the card, blink the error LED...
if(num_bits <=0){digitalWrite(ERROR_LED, HIGH);delay(250);digitalWrite(ERROR_LED, LOW);}else{
Serial.print("Return Data: ");
Serial.println(data);}reset_data();}